package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/armon/go-radix"
	"github.com/sirupsen/logrus"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"
)

// 默认连接池
var defaultTransport = &http.Transport{
	// 增加总连接数
	MaxIdleConns: 1000,
	// 提高单主机连接数
	MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) * 50,
	// 适当延长空闲时间
	IdleConnTimeout: 90 * time.Second,
	// 缩短TLS握手时间
	TLSHandshakeTimeout: 5 * time.Second,
	// 添加Expect头超时
	ExpectContinueTimeout: 1 * time.Second,
	// 启用HTTP/2
	ForceAttemptHTTP2: true,
	DialContext: (&net.Dialer{
		Timeout:   15 * time.Second,
		KeepAlive: 30 * time.Second,
		// 启用双栈
		DualStack: true,
	}).DialContext,
	// 开启长连接
	DisableKeepAlives: false,
	TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
	Proxy: func(req *http.Request) (*url.URL, error) {
		if config.ProxyConfig.EnableProxy == true {
			return parseProxyURLWithAuth()
		}
		return nil, nil
	},
}

var routeTree *radix.Tree

var bufferPool = sync.Pool{
	New: func() interface{} {
		return bytes.NewBuffer(make([]byte, 0, 4096))
	},
}

// 初始化 http服务器
func initHttpServer() {
	if config.Routes == nil {
		logger.Fatal("代理路径不能为空，请配置代理路径")
	}

	// 预编译所有正则表达式
	routeTree = radix.New()
	for i := range config.Routes {
		route := config.Routes[i]
		if route.Rewrite != "" {
			pattern := strings.Split(route.Rewrite, " -> ")[0]
			route.rewriteRegex = regexp.MustCompile(pattern)
		}
		routeTree.Insert(route.PathPrefix, &route)
	}

	// 使用配置端口
	server := &http.Server{
		Addr: fmt.Sprintf(":%d", config.ServerConfig.Port),
	}
	logger.Infof("代理服务器已在「%d」端口上启动", config.ServerConfig.Port)

	router := http.NewServeMux()
	router.HandleFunc("/", handleRequest(config.Routes))
	server.Handler = router
	// 启动http服务器
	logger.Fatal(server.ListenAndServe())
}

// 根据配置路由转发请求
func handleRequest(routes []Route) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if routes == nil {
			logger.Error("未找到可用的路由列表，无法进行请求代理")
			return
		}

		// 1. 路由匹配
		var matchedRoute *Route

		path := r.URL.Path
		_, value, found := routeTree.LongestPrefix(path)

		if !found {
			logger.Errorf("请求路径『%s』为匹配到可用代理配置，请检查后再次重试", r.URL.Path)
			http.Error(w, "No route matched", http.StatusNotFound)
			return
		}

		matchedRoute = value.(*Route)

		// 2. 路径重写
		targetPath := strings.TrimPrefix(r.URL.Path, matchedRoute.PathPrefix)
		if matchedRoute.rewriteRegex != nil {
			parts := strings.Split(matchedRoute.Rewrite, " -> ")
			if len(parts) == 2 {
				targetPath = matchedRoute.rewriteRegex.ReplaceAllString(
					r.URL.Path,
					strings.TrimSpace(parts[1]),
				)
			}
		}

		// 3. 构建目标URL
		targetURL, _ := url.Parse(matchedRoute.Target)
		targetURL.Path = singleJoiningSlash(targetURL.Path, targetPath)

		logger.Infof("请求路径『%s』已匹配到可用代理规则 『%s』，开始尝试发起代理", r.URL.Path, targetURL.String())

		// 4. 创建反向代理
		// 修改请求处理逻辑
		proxy := httputil.NewSingleHostReverseProxy(targetURL)
		proxy.Transport = defaultTransport
		proxy.Director = func(req *http.Request) {
			// 保留原始请求头
			for key, values := range r.Header {
				req.Header[key] = values
			}

			// 保留原始请求路径和查询参数
			req.URL.Scheme = targetURL.Scheme
			req.URL.Host = targetURL.Host
			req.URL.Path = targetURL.Path
			req.Host = targetURL.Host
			// 保留原始请求的查询参数
			req.URL.RawQuery = r.URL.RawQuery

			// 保留所有请求头（包括Content-Type）
			req.Header.Set("X-Forwarded-For", req.RemoteAddr)
			logger.Debugf("Original Path: %s", r.URL.Path)
			logger.Debugf("Rewritten Path: %s", targetPath)
			logger.Debugf("Final Target URL: %s", targetURL.String())
		}

		// 配置错误处理
		proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
			logger.WithFields(logrus.Fields{
				"客户端请求路径": r.RemoteAddr,
				"客户端用户代理": r.UserAgent(),
				"客户端请求参数": formatRequestParams(r),
				"客户端请求头":  formatRequestHeaders(r),
				"客户端请求类型": r.Method,
			}).Errorf("代理客户端的「%s」%s 请求到 %s 出现异常，异常信息为：「%v，请求相关内容：",
				r.Method,
				r.URL.Path,
				targetURL.String(),
				err)
			w.WriteHeader(http.StatusBadGateway)
			w.Write([]byte(err.Error()))
		}

		proxy.ModifyResponse = func(resp *http.Response) error {
			logger.WithFields(logrus.Fields{
				"客户端请求路径": r.RemoteAddr,
				"客户端用户代理": r.UserAgent(),
				"客户端请求参数": formatRequestParams(r),
				"客户端请求头":  formatRequestHeaders(r),
				"客户端请求类型": r.Method,
				"响应结果：":   formatResponseBody(resp),
			}).Infof("完成代理客户端的「%s」%s 到 %s：请求和响应的相关内容：",
				r.Method,
				r.URL.Path,
				targetURL.String())

			return nil
		}

		// 复制请求体（支持文件上传等大数据量场景）
		r.Body = http.MaxBytesReader(w, r.Body, 10<<20)

		// 5. 记录访问日志
		logger.WithFields(logrus.Fields{
			"客户端请求路径": r.RemoteAddr,
			"客户端用户代理": r.UserAgent(),
			"客户端请求参数": formatRequestParams(r),
			"客户端请求头":  formatRequestHeaders(r),
			"客户端请求类型": r.Method,
		}).Infof(" 开始代理客户端的「%s」%s 到 %s。本次请求相关内容：",
			r.Method,
			r.URL.Path,
			targetURL.String())
		// 6. 执行代理
		proxy.ServeHTTP(w, r)
	}
}

func singleJoiningSlash(a, b string) string {
	a = strings.TrimSuffix(a, "/")
	b = strings.TrimPrefix(b, "/")
	if a == "" {
		return "/" + b
	}
	return a + "/" + b
}

// 设置代理服务器
func parseProxyURLWithAuth() (*url.URL, error) {
	if config.ProxyConfig.EnableProxy {
		logger.Info("使用代理服务器发起请求")
		urli := url.URL{}
		urlproxy, err := urli.Parse(config.ProxyConfig.ProxyUrl)
		if err != nil {
			return nil, err
		}
		// 如果代理服务器的账号密码设置不为空，则使用账号密码进行登录
		if config.ProxyConfig.ProxyUrlUsername != "" && config.ProxyConfig.ProxyUrlPassword != "" {
			urlproxy.User = url.UserPassword(
				config.ProxyConfig.ProxyUrlUsername,
				config.ProxyConfig.ProxyUrlPassword,
			)
		}
		return urlproxy, nil
	}

	logger.Info("不使用代理服务器发起请求")
	return nil, nil
}

// 获取请求参数字符串，用于日志输出
func formatRequestParams(req *http.Request) string {
	buf := bufferPool.Get().(*bytes.Buffer)
	defer bufferPool.Put(buf)
	buf.Reset()

	// 根据请求类型获取不同的请求参数
	switch req.Method {
	case "GET":
		buf.WriteString(req.URL.Query().Encode())
	case "POST", "PUT", "PATCH":
		// 尝试从请求体中获取参数
		if req.Body != nil {
			bodyBytes, _ := io.ReadAll(io.TeeReader(req.Body, buf))
			req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
		}
	}
	return buf.String()
}

// 获取响应结果
func formatResponseBody(resp *http.Response) string {
	// 尝试从响应体中获取参数
	if resp.Body != nil {
		bodyBytes, err := io.ReadAll(resp.Body)
		if err == nil {
			// 重新设置响应体
			resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
			return string(bodyBytes)
		}
	}
	return ""
}

// 获取请求头字符串，用于日志输出
func formatRequestHeaders(req *http.Request) string {
	// 遍历获取请求头，并用map存储
	headers := make(map[string]string)
	for key, values := range req.Header {
		headers[key] = strings.Join(values, ", ")
	}
	// 转换成json字符串输出
	jsonHeaders, err := json.Marshal(headers)
	if err != nil {
		return ""
	}
	return string(jsonHeaders)
}
